from functools import partial
from math import atan2, degrees

import shiboken6
from PySide6.QtGui import QPen, Qt, QAction
from PySide6.QtWidgets import QGraphicsPathItem, QGraphicsPolygonItem, QMenu

from 程序核心代码.mvvm重构.基础定义.基础图形项.连接线.连接线动画效果 import 路径动画
from 程序核心代码.mvvm重构.基础定义.基础图形项.连接线.连接线箭头 import 箭头
from 程序核心代码.mvvm重构.基础定义.基础图形项.连接线.连接线路径计算 import 路径计算器


class 连接线基础图形项(QGraphicsPathItem):
    """
    连接线类，用于表示工作流中的连接线。

    Attributes:
        起点接口 (QGraphicsItem): 连接线的起点。
        结束接口 (QGraphicsItem): 连接线的终点。
        样式 (str): 连接线的样式，默认为"曲线"。
        颜色 (QColor): 连接线的颜色，默认为绿色。
        方向箭头大小 (int): 方向箭头的大小，默认为10。
        箭头项 (QGraphicsPolygonItem): 方向箭头的图形项。
        动画效果 (路径动画): 连接线的动画效果。
    """

    # 常量定义
    默认样式 = "曲线"
    默认颜色 = Qt.green  # 默认颜色为绿色
    默认箭头大小 = 20
    画笔宽度 = 5

    def __init__(self, 连接线模型, 样式=默认样式, 颜色=默认颜色, 双向: bool = False, 方向箭头大小=默认箭头大小,
                 可删除=True, 父项=None):
        """
        初始化连接线对象。

        Args:
            连接线模型: 节点关系对象，包含起始接口和结束接口。
            样式 (str, optional): 连接线的样式，默认为"曲线"。
            颜色 (QColor, optional): 连接线的颜色，默认为绿色。
            双向 (bool, optional): 连接线是否为双向。
            方向箭头大小 (int, optional): 方向箭头的大小，默认为20。
            可删除 (bool, optional): 连接线是否可删除，默认为True。
            父项 (QGraphicsItem, optional): 父图形项。
        """
        super().__init__(父项)  # 调用父类的初始化方法
        self.父项 = 父项
        self.连接线模型 = 连接线模型
        self.连接线模型.图形项=self
        self.起点接口 = self.连接线模型.起始接口  # 设置起点属性
        self.结束接口 = self.连接线模型.结束接口  # 设置终点属性
        self.可删除 = 可删除

        self.起点图形项 = self.起点接口.图形项.连接点
        self.终点图形项 = self.结束接口.图形项.连接点

        self.样式 = 样式  # 设置样式属性
        self.颜色 = 颜色  # 设置颜色属性
        self.方向箭头大小 = 方向箭头大小  # 设置方向箭头大小属性
        self.setPen(QPen(self.颜色, self.画笔宽度))  # 设置画笔颜色和宽度
        self.箭头项 = 箭头(大小=self.方向箭头大小, 颜色=self.颜色, 父项=self.父项)

        self.动画效果 = None  # 初始化动画效果属性为None
        self.数据流向 = 双向
        self.路径动画 = None
        self.连接线图形项刷新()  # 初始化位置
        self.setCacheMode(QGraphicsPathItem.NoCache)
    def 设置样式(self, 样式):
        """
        设置连接线的样式。

        Args:
            样式 (str): 连接线的样式。
        """
        self.样式 = 样式  # 更新样式属性
        self.连接线图形项刷新()  # 更新位置

    def 设置颜色(self, 颜色):
        """
        设置连接线的颜色。

        Args:
            颜色 (QColor): 连接线的颜色。
        """
        self.颜色 = 颜色  # 更新颜色属性
        self.setPen(QPen(self.颜色, self.画笔宽度))  # 设置画笔颜色和宽度
        self.连接线图形项刷新()  # 更新位置

    def 连接线图形项刷新(self):
        """
        更新连接线的位置和路径，同时初始化或更新箭头的路径动画。
        """
        # 检查当前图形项 是子项还是场景
        if self.父项 is None:
            # 父项为空，则说明连接线是场景中的一个图形项更新位置策略需要更换
            # 两种更新位置策略：
            # 1. 基于父项的局部坐标 更新位置  (用于节点容器QGraphicsWidget内部的连接线)
            # 1.1 被连接的节点为简化模式 其获取的连接点图形项不再基于实际端口位置，而是基于节点简化端口的位置
            # 1.2 被连接的节点为非简化模式 其获取的起始图形项基于实际端口位置
            # 2. 基于场景的全局坐标 更新位置  (用于场景内跨节点容器的连接线)
            # 2.1  特殊情形 节点容器 为 场景 而非 QGraphicsWidget 其获取的连接点图形项基于场景的全局坐标
            # 3. 基于场景的局部坐标 更新位置  (用于跨场景的连接线) 此时需要将连接线交于 更上层级的容器的场景进行管理

            print(f"连接线 {self} 的父项为空，无法刷新")
            return
        # 获取场景坐标
        if self.起点图形项 is not None and shiboken6.isValid(self.起点图形项) and \
                self.终点图形项 is not None and shiboken6.isValid(self.终点图形项):
            起点场景位置 = self.起点图形项.scenePos()
            终点场景位置 = self.终点图形项.scenePos()

            # 将场景坐标转换为它们父节点的局部坐标
            起点位置 = self.父项.mapFromScene(起点场景位置)
            终点位置 = self.父项.mapFromScene(终点场景位置)
            路径 = 路径计算器.计算路径(self.样式, 起点位置, 终点位置)

            self.setPath(路径)

            if self.scene():
                if not self.路径动画:  # 初次设置路径动画
                    self.箭头项.setParentItem(self.父项)
                    self.路径动画 = 路径动画(self.箭头项, self.path(), 基础速度=0.3, 双向=self.数据流向 , 父项=self)
                else:  # 更新路径动画
                    self.路径动画.路径 = self.path()
                    self.路径动画.路径长度 = self.路径动画.计算路径长度()
                    self.路径动画.持续时间 = self.路径动画.路径长度 / self.路径动画.基础速度
            self.update()

    def 更新箭头(self):
        """
        更新箭头的位置和方向。
        """
        路径 = self.path()
        终点位置 = 路径.pointAtPercent(1.0)
        倒数第二点位置 = 路径.pointAtPercent(0.99)

        # 计算箭头的旋转角度
        角度 = degrees(atan2(终点位置.y() - 倒数第二点位置.y(),
                            终点位置.x() - 倒数第二点位置.x()))

        self.箭头项.setPos(终点位置)
        self.箭头项.setRotation(角度)

    def 删除(self):
        """
        删除连接线及其相关对象。
        """
        #print(f"正在删除连接线: {起始接口}")  # 调试信息

        # 移除箭头项
        if self.箭头项:
            if self.箭头项.scene():
                self.箭头项.scene().removeItem(self.箭头项)
                #print("箭头项已从场景中移除")
            self.箭头项.setParentItem(None)
            self.箭头项 = None

        # 移除路径动画
        if self.路径动画:
            self.路径动画.停止()  # 停止动画和定时器
            self.路径动画 = None
            #print("路径动画已停止并移除")

        # # 解除与节点的关系
        # if 起始接口.连接线模型定义:
        #     起始接口.连接线模型定义.删除关系()
        #     起始接口.连接线模型定义 = None
        #     #print("已解除与节点的关系")

        # 从场景中移除连接线
        if self.scene():
            self.scene().removeItem(self)
            #print("连接线已从场景中移除")

        # 解除父子关系
        self.setParentItem(None)

        # 清空其他引用
        self.起点接口 = None
        self.结束接口 = None
        self.起点图形项 = None
        self.终点图形项 = None
        self.样式 = None
        self.颜色 = None
        self.路径动画 = None
        self.update()
        #print(f"连接线图形项 {起始接口} 删除完成")

    def contextMenuEvent(self, event):
        """
        右键菜单事件处理方法。

        Args:
            event (QGraphicsSceneContextMenuEvent): 右键菜单事件。
        """
        菜单 = QMenu()  # 创建菜单
        if self.可删除:
            删除动作 = QAction("删除连接线")  # 创建删除动作
            删除动作.triggered.connect(self.连接线模型.删除关系)  # 连接删除动作到删除方法
            菜单.addAction(删除动作)  # 将删除动作添加到菜单

        样式动作 = {  # 样式动作字典
            "直线": "直线",
            "曲线": "曲线",
            "折线": "折线",
            "弧线": "弧线"
        }
        动作列表 = []
        for 样式 in 样式动作:
            if self.样式 != 样式:
                切换动作 = QAction(f"切换到{样式}")  # 创建切换动作
                切换动作.triggered.connect(partial(self.设置样式, 样式))  # 连接切换动作的槽函数
                动作列表.append(切换动作)
                菜单.addAction(切换动作)  # 将切换动作添加到菜单

        菜单.exec(event.screenPos())  # 在鼠标位置显示菜单

    def 更改流向(self, 双向: bool):
        """
        更改连接线的流向。

        Args:
            双向 (bool): 是否为双向流向。
        """
        self.数据流向 = 双向
        self.更新箭头()  # 更新箭头以反映新的流向
    # def paint(起始接口, painter, option, widget=None):
    #     #painter.setRenderHint(QPainter.Antialiasing, True)
    #     super().paint(painter, option, widget)
    #     # 输出当前渲染后端的名称
    #     print(f"{起始接口.__class__.__name__}当前渲染后端: {painter.paintEngine().type()}")
    #     # 输出当前的父项类型
    #     if 起始接口.父项:
    #         print(f"{起始接口.__class__.__name__}的父项类型: {起始接口.父项.__class__.__name__}")
    #         # 输出父项的渲染后端名称
    #         if hasattr(起始接口.父项, "渲染后端"):
    #             print(f"{起始接口.父项.__class__.__name__}的渲染后端: {起始接口.父项.渲染后端}")
    #
    #     else:
    #         print(f"{起始接口.__class__.__name__}没有父项")
